<?php
namespace app\index\controller;

use app\common\model\AttachMain;
use app\common\model\Debug;
use app\common\model\UserDownloadCount;
use app\common\model\UserDownloadLog;
use app\common\model\UserDownloadTimes;
use app\common\model\UserMain;
use app\common\model\UserSiteAccess;
use app\common\model\WebSite;
use OSS\OssClient;
use think\facade\App;
use think\facade\Db;
use think\facade\Request;

class Download
{

    private function download_count()
    {
        global $_G;
        if (empty($_G['uid'])) {
            return [];
        }
        $result = [];
        foreach (UserDownloadCount::where('uid', '=', $_G['uid'])->select() as $data) {
            $result[$data['site_id']] = $data;
        }
        return $result;
    }

    public function index($attach_id = 0)
    {
        global $_G;
        if (empty($_G['uid'])) {
            return show_error('请先登录在操作');
        }

        $last_log = UserDownloadLog::where('uid', '=', $_G['uid'])->order('log_id', 'desc')->find();
        if (!empty($_G['setting']['parse_space_time']) && intval($_G['setting']['parse_space_time']) > 0 && !empty($last_log)) {

            $has_time = intval($_G['setting']['parse_space_time']) - (intval(Request::time()) - $last_log->getData('create_time'));
            if ($has_time > 0) {
                //return show_error('操作过于频繁，请 <strong class="text-danger">' . $has_time . '</strong> 秒后后再试');
            }
        }

        $savename = '';
        if (empty($attach_id) || !$attach = AttachMain::where('attach_id', '=', $attach_id)->find()) {
            return show_error('附件不存在');
        }
        $web_site = WebSite::where('site_id', '=', $attach['site_id'])->find();
        $log      = UserDownloadLog::where('uid', '=', $_G['uid'])->where('site_id', '=', $attach['site_id'])->where('parse_url', '=', $attach['request_url'])->find();
        if (empty($log) || (!empty($log) && ((Request::time() - strtotime($log['create_time'])) >= 3600))) {

            $site_access = UserSiteAccess::where([['uid', '=', $_G['uid']], ['site_id', '=', $web_site['site_id']]])->find();

            $error = '';
            if ($_G['user']['parse_max_times'] < 0) {
                $error = '您的账户没有解析权限';
            }
            if (!$error && $_G['user']['parse_max_times'] > 0 && $_G['user']['parse_times'] >= $_G['user']['parse_max_times']) {
                $error = '您的账户解析次数已达上限，请充值';
            }
            if (!$error && empty($site_access)) {
                $error = '您尚未开通该站点的解析权限';
            }
            if (!$error && ($site_access['day_times'] < 0 || $site_access['week_times'] < 0 || $site_access['month_times'] < 0 || $site_access['year_times'] < 0 || $site_access['max_times'] < 0 || $site_access->getData('out_time') < 0)) {
                $error = '该站点权限已被禁用，请升级账户权限';
            }
            if (!$error && $site_access->getData('out_time') > 0 && $site_access->getData('out_time') < Request::time()) {
                $error = '该站点解析权限已过期';
            }

            $download_count = $this->download_count();
            if (!empty($download_count[$web_site['site_id']])) {
                if (!$error && $site_access['day_times'] > 0 && $download_count[$web_site['site_id']]['day_used_time'] >= $site_access['day_times']) {
                    $error = '该站点今日解析次数已达上限，请明日再来或升级帐号权限';
                }
                if (!$error && $site_access['week_times'] > 0 && $download_count[$web_site['site_id']]['week_used_time'] >= $site_access['week_times']) {
                    $error = '该站点本周解析次数已达上限，请下周再来或升级帐号权限';
                }
                if (!$error && $site_access['month_times'] > 0 && $download_count[$web_site['site_id']]['month_used_time'] >= $site_access['month_times']) {
                    $error = '该站点本月解析次数已达上限，请下月再来或升级帐号权限';
                }
                if (!$error && $site_access['year_times'] > 0 && $download_count[$web_site['site_id']]['year_used_time'] >= $site_access['year_times']) {
                    $error = '该站点今年解析次数已达上限，请明年再来或升级帐号权限';
                }
            }
            if (!$error && $site_access['max_times'] > 0 && $site_access['parse_times'] >= $site_access['max_times']) {
                $error = '该站点总解析次数已达上限，请升级账号权限';
            }
            if (!empty($error)) {
                $download_times = UserDownloadTimes::where([['uid', '=', $_G['uid']], ['site_id', '=', $web_site['site_id']], ['status', '=', 1]])->whereColumn('times', '>', 'used_times')->find();
                if (empty($download_times)) {
                    return show_error($error);
                }
            }
            UserDownloadLog::create([
                'uid'       => $_G['uid'],
                'site_id'   => $web_site['site_id'],
                'parse_url' => $attach['request_url'],
                'type'      => empty($download_times) ? 'vip' : 'download-times',
                'use_times' => 1,
                'status'    => 1,
            ]);

            if (!empty($download_times)) {
                if (($download_times->used_times + 1) >= $download_times->times) {
                    $download_times->status = 0;
                }
                $download_times->used_times = Db::raw('used_times+1');
                $download_times->save();
            } else {
                UserMain::where('uid', '=', $_G['uid'])->update(['parse_times' => Db::raw('parse_times+1')]);
                $count = UserDownloadCount::where([['uid', '=', $_G['uid']], ['site_id', '=', $web_site['site_id']]])->find();
                if (!empty($count)) {
                    $count->day_used_time   = Db::raw('day_used_time+1');
                    $count->week_used_time  = Db::raw('week_used_time+1');
                    $count->month_used_time = Db::raw('month_used_time+1');
                    $count->year_used_time  = Db::raw('year_used_time+1');
                    $count->save();
                } else {
                    UserDownloadCount::create([
                        'uid'                => $_G['uid'],
                        'site_id'            => $web_site['site_id'],
                        'day_used_time'      => 1,
                        'week_used_time'     => 1,
                        'month_used_time'    => 1,
                        'year_used_time'     => 1,
                        'day_refresh_time'   => date('Ymd'),
                        'week_refresh_time'  => date('W'),
                        'month_refresh_time' => date('Ym'),
                        'year_refresh_time'  => date('Y'),
                    ]);
                }
                if (!empty($site_access)) {
                    $site_access->parse_times = Db::raw('parse_times+1');
                    $site_access->save();
                }
            }
        }
        set_time_limit(0);
        ini_set('memory_limit', '480M');

        $debug = new Debug;
        $debug->remark('begin');

        $cookie_file = App::getConfigPath() . '/site_cookie/cookie_' . $attach->attach_id;
        try {
            ob_end_clean();
            header('Pragma: public');
            header('Content-type: application/octet-stream');
            header('Accept-Ranges: bytes');
            header('Content-Transfer-Encoding: binary');
            $url_str = urldecode(urldecode($attach->response_url));
            preg_match('/attachment; filename="(.*?)"; filename/', $url_str, $filename);
            if (!empty($filename['1'])) {
                $savename = $filename['1'];
                header('Content-Disposition: attachment; filename="' . $savename . '"');
            }
            $write_stream = fopen($attach->local_file, 'w');
            $curl         = curl_init();
            curl_setopt($curl, CURLOPT_TIMEOUT, 3600);
            curl_setopt($curl, CURLOPT_URL, $attach->response_url);
            curl_setopt($curl, CURLOPT_HEADER, false);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36');
            curl_setopt($curl, CURLOPT_HTTPHEADER, [
                'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
                'Accept-Encoding: gzip, deflate',
                'Accept-Language: zh-CN,zh;q=0.9',
                'Cache-Control: max-age=0',
                'Connection: keep-alive',
                'Upgrade-Insecure-Requests: 1',
            ]);
            curl_setopt($curl, CURLOPT_ENCODING, 'gzip');
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            if (is_file($cookie_file)) {
                curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie_file);
                curl_setopt($curl, CURLOPT_COOKIEFILE, $cookie_file);
            }
            curl_setopt($curl, CURLOPT_WRITEFUNCTION, function ($curl, $data) use ($write_stream, $attach, &$savename) {

                $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
                if (intval($code) / 100 != 2) {
                    return strlen($data);
                }

                $length        = strlen($data);
                $written_total = 0;
                $written_last  = 0;

                while ($written_total < $length) {
                    $written_last = fwrite($write_stream, substr($data, $written_total));

                    if ($written_last === false) {
                        return $written_total;
                    }

                    $written_total += $written_last;
                }

                if (empty($savename)) {
                    $bin      = substr($data, 0, 2);
                    $strInfo  = @unpack('C2chars', $bin);
                    $typeCode = intval($strInfo['chars1'] . $strInfo['chars2']);
                    $typeList = [
                        7790   => 'exe',
                        7784   => 'midi',
                        8297   => 'rar',
                        255216 => 'jpg',
                        7173   => 'gif',
                        6677   => 'bmp',
                        13780  => 'png',
                        8075   => 'zip',
                        4949   => 'tar',
                        55122  => '7z',
                        5666   => 'psd',
                    ];
                    $savename = $attach->attach_id . '.' . (empty($typeList[$typeCode]) ? $typeCode : $typeList[$typeCode]);
                    header('Content-Disposition: attachment; filename="' . $savename . '"');
                }
                echo $data;
                return $written_total;
            });
            $result = curl_exec($curl);
            $info   = curl_getinfo($curl);
            curl_close($curl);

            fclose($write_stream);
            if ($info['http_code'] == 200) {

                $debug->remark('end');
                if (is_file($attach->local_file)) {
                    $attach->savename      = $savename;
                    $attach->filesize      = filesize($attach->local_file);
                    $attach->download_time = $debug->getRangeTime('begin', 'end');
                    $attach->status        = 2;
                    $attach->save();
                }
            }

        } catch (\Exception $e) {
            return show_error('文件下载错误');
        }
        exit;
    }

    public function cache($attach_id = 0)
    {
        global $_G;
        if (empty($_G['uid'])) {
            return show_error('请先登录在操作');
        }

        $last_log = UserDownloadLog::where('uid', '=', $_G['uid'])->order('log_id', 'desc')->find();
        if (!empty($_G['setting']['parse_space_time']) && intval($_G['setting']['parse_space_time']) > 0 && !empty($last_log)) {

            $has_time = intval($_G['setting']['parse_space_time']) - (intval(Request::time()) - $last_log->getData('create_time'));
            if ($has_time > 0) {
                //return show_error('操作过于频繁，请 <strong class="text-danger">' . $has_time . '</strong> 秒后后再试');
            }
        }
        if (empty($attach_id) || !$attach = AttachMain::where('attach_id', '=', $attach_id)->find()) {
            return show_error('附件不存在');
        }

        if ($attach['status'] < 2) {
            return show_error('附件尚未缓存');
        }
        $web_site = WebSite::where('site_id', '=', $attach['site_id'])->find();
        $log      = UserDownloadLog::where('uid', '=', $_G['uid'])->where('site_id', '=', $attach['site_id'])->where('parse_url', '=', $attach['request_url'])->find();
        if (empty($log) || (!empty($log) && ((Request::time() - strtotime($log['create_time'])) >= 3600))) {

            $site_access = UserSiteAccess::where([['uid', '=', $_G['uid']], ['site_id', '=', $web_site['site_id']]])->find();

            $error = '';
            if ($_G['user']['parse_max_times'] < 0) {
                $error = '您的账户没有解析权限';
            }
            if (!$error && $_G['user']['parse_max_times'] > 0 && $_G['user']['parse_times'] >= $_G['user']['parse_max_times']) {
                $error = '您的账户解析次数已达上限，请充值';
            }
            if (!$error && empty($site_access)) {
                $error = '您尚未开通该站点的解析权限';
            }
            if (!$error && ($site_access['day_times'] < 0 || $site_access['week_times'] < 0 || $site_access['month_times'] < 0 || $site_access['year_times'] < 0 || $site_access['max_times'] < 0 || $site_access->getData('out_time') < 0)) {
                $error = '该站点权限已被禁用，请升级账户权限';
            }
            if (!$error && $site_access->getData('out_time') > 0 && $site_access->getData('out_time') < Request::time()) {
                $error = '该站点解析权限已过期';
            }

            $download_count = $this->download_count();
            if (!empty($download_count[$web_site['site_id']])) {
                if (!$error && $site_access['day_times'] > 0 && $download_count[$web_site['site_id']]['day_used_time'] >= $site_access['day_times']) {
                    $error = '该站点今日解析次数已达上限，请明日再来或升级帐号权限';
                }
                if (!$error && $site_access['week_times'] > 0 && $download_count[$web_site['site_id']]['week_used_time'] >= $site_access['week_times']) {
                    $error = '该站点本周解析次数已达上限，请下周再来或升级帐号权限';
                }
                if (!$error && $site_access['month_times'] > 0 && $download_count[$web_site['site_id']]['month_used_time'] >= $site_access['month_times']) {
                    $error = '该站点本月解析次数已达上限，请下月再来或升级帐号权限';
                }
                if (!$error && $site_access['year_times'] > 0 && $download_count[$web_site['site_id']]['year_used_time'] >= $site_access['year_times']) {
                    $error = '该站点今年解析次数已达上限，请明年再来或升级帐号权限';
                }
            }
            if (!$error && $site_access['max_times'] > 0 && $site_access['parse_times'] >= $site_access['max_times']) {
                $error = '该站点总解析次数已达上限，请升级账号权限';
            }
            if (!empty($error)) {
                $download_times = UserDownloadTimes::where([['uid', '=', $_G['uid']], ['site_id', '=', $web_site['site_id']], ['status', '=', 1]])->whereColumn('times', '>', 'used_times')->find();
                if (empty($download_times)) {
                    return show_error($error);
                }
            }
            UserDownloadLog::create([
                'uid'       => $_G['uid'],
                'site_id'   => $web_site['site_id'],
                'parse_url' => $attach['request_url'],
                'type'      => empty($download_times) ? 'vip' : 'download-times',
                'use_times' => 1,
                'status'    => 1,
            ]);

            if (!empty($download_times)) {
                if (($download_times->used_times + 1) >= $download_times->times) {
                    $download_times->status = 0;
                }
                $download_times->used_times = Db::raw('used_times+1');
                $download_times->save();
            } else {
                UserMain::where('uid', '=', $_G['uid'])->update(['parse_times' => Db::raw('parse_times+1')]);
                $count = UserDownloadCount::where([['uid', '=', $_G['uid']], ['site_id', '=', $web_site['site_id']]])->find();
                if (!empty($count)) {
                    $count->day_used_time   = Db::raw('day_used_time+1');
                    $count->week_used_time  = Db::raw('week_used_time+1');
                    $count->month_used_time = Db::raw('month_used_time+1');
                    $count->year_used_time  = Db::raw('year_used_time+1');
                    $count->save();
                } else {
                    UserDownloadCount::create([
                        'uid'                => $_G['uid'],
                        'site_id'            => $web_site['site_id'],
                        'day_used_time'      => 1,
                        'week_used_time'     => 1,
                        'month_used_time'    => 1,
                        'year_used_time'     => 1,
                        'day_refresh_time'   => date('Ymd'),
                        'week_refresh_time'  => date('W'),
                        'month_refresh_time' => date('Ym'),
                        'year_refresh_time'  => date('Y'),
                    ]);
                }
                if (!empty($site_access)) {
                    $site_access->parse_times = Db::raw('parse_times+1');
                    $site_access->save();
                }
            }
        }
        $doesExist = false;
        if (!is_file($attach['savename']) && !is_file($attach['local_file'])) {
            if ($_G['setting']['AccessKeyId'] && $_G['setting']['AccessKeySecret'] && $_G['setting']['Endpoint'] && $_G['setting']['Bucket']) {
                try {
                    $ossClient = new OssClient($_G['setting']['AccessKeyId'], $_G['setting']['AccessKeySecret'], $_G['setting']['Endpoint'], false);

                } catch (\Exception $e) {

                }
                if (!empty($ossClient)) {
                    $doesExist = $ossClient->doesObjectExist($_G['setting']['Bucket'], $attach['savename']) || $ossClient->doesObjectExist($_G['setting']['Bucket'], $attach['local_file']);
                }
            }
            if (empty($doesExist)) {
                return show_error('附件文件丢失，请联系管理员');
            }
        }
        if ($doesExist) {
            ob_end_clean();
            header("Content-type: application/octet-stream");
            header("Content-Transfer-Encoding: binary");
            header("Accept-Ranges: bytes");
            header("Content-Length: " . $attach['filesize']);
            header("Content-Disposition: attachment; filename=\"" . ($attach['filename'] ?: $attach['savename']) . "\"");
            $start_range = 0;
            while ($start_range < $attach['filesize']) {
                $end_range = $start_range + 102400;
                $end_range = $end_range >= $attach['filesize'] ? $attach['filesize'] - 1 : $end_range;
                echo $ossClient->getObject($doesExist == 'setting' ? $_G['setting']['Bucket'] : 'attach', $attach['savename'], [
                    'range' => $start_range . '-' . $end_range,
                ]);
                $start_range = $end_range + 1;
            }
            exit;
        } else if (is_file($attach['savename'])) {
            return download($attach['savename'], (!empty($attach['filename']) ? $attach['filename'] : $attach['savename']));
        } else if (is_file($attach['local_file'])) {
            return download($attach['local_file'], (!empty($attach['filename']) ? $attach['filename'] : $attach['savename']));
        }
    }
}
